package com.ilikesoup.instant.executor;

import com.segi.uhomecp.park.access.util.lock.MultiKeyLock;

import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * 互斥键集放在Redis中，以实现多进程互斥键集共享
 *
 * @author soup
 */
@SuppressWarnings("rawtypes")
public class RedisKeyPickThreadPoolExecutor extends AbstractKeyPickThreadPoolExecutor {

    private final MultiKeyLock multiKeyLock;

    @Override
    protected void removeKeys(Collection<?> keys) {
        multiKeyLock.unlockMulti(convertKeys(keys));
    }

    @Override
    protected boolean addKeysIfNotExists(Collection<?> keys) {
        return multiKeyLock.tryLockMulti(convertKeys(keys));
    }

    protected String[] convertKeys(Collection<?> keys) {
        return keys.stream().map(Objects::toString).toArray(String[]::new);
    }

    @Override
    public void clearKeys() {
        multiKeyLock.unlockAll();
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                                           RejectedExecutionHandler handler, String scannerThreadName, long scannerThreadAwakeTimeout, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler, scannerThreadName, scannerThreadAwakeTimeout);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                                           RejectedExecutionHandler handler, BlockingQueue<KeyPickRunnableFuture> waitingQueue, String scannerThreadName, long scannerThreadAwakeTimeout, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler, waitingQueue, scannerThreadName, scannerThreadAwakeTimeout);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    private RedisKeyPickThreadPoolExecutor(int corePoolSize, int maximumPoolSize, BlockingQueue<Runnable> workQueue, BlockingQueue<KeyPickRunnableFuture> waitingQueue, String scannerThreadName, MultiKeyLock multiKeyLock) {
        super(corePoolSize, maximumPoolSize, workQueue, waitingQueue, scannerThreadName);
        this.multiKeyLock = Objects.requireNonNull(multiKeyLock);
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, multiKeyLock);
        executor.start();
        return executor;
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler, multiKeyLock);
        executor.start();
        return executor;
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, multiKeyLock);
        executor.start();
        return executor;
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler, multiKeyLock);
        executor.start();
        return executor;
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                                                        RejectedExecutionHandler handler, String scannerThreadName, long scannerThreadAwakeTimeout, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler, scannerThreadName, scannerThreadAwakeTimeout, multiKeyLock);
        executor.start();
        return executor;
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                                                        RejectedExecutionHandler handler, BlockingQueue<KeyPickRunnableFuture> waitingQueue, String scannerThreadName, long scannerThreadAwakeTimeout, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler, waitingQueue, scannerThreadName, scannerThreadAwakeTimeout, multiKeyLock);
        executor.start();
        return executor;
    }

    public static RedisKeyPickThreadPoolExecutor create(int corePoolSize, int maximumPoolSize, BlockingQueue<Runnable> workQueue, BlockingQueue<KeyPickRunnableFuture> waitingQueue, String scannerThreadName, MultiKeyLock multiKeyLock) {
        RedisKeyPickThreadPoolExecutor executor = new RedisKeyPickThreadPoolExecutor(corePoolSize, maximumPoolSize, workQueue, waitingQueue, scannerThreadName, multiKeyLock);
        executor.start();
        return executor;
    }

}
